package org.noip.milhous;

import java.lang.reflect.Field;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.noip.milhous.annotations.autoincrement;
import org.noip.milhous.annotations.length;
import org.noip.milhous.annotations.primarykey;
import org.noip.milhous.annotations.transientVar;
import org.noip.milhous.clause.OrderBy;
import org.noip.milhous.clause.Utilities;
import org.noip.milhous.clause.Where;
import org.noip.milhous.clause.Where.OPERATOR;

/**
 * The table class maps to a table in the database. The table can create updates and selects to and from the database
 * 
 * @author Matthew Miller
 * 
 * @param <Type>
 *          the java class the table is associated with
 */
public class DatabaseTable<Type> extends DatastoreObject<Type>
{
	private static final String DELETE_FROM = "DELETE FROM ";

	private static final String update = " ON DUPLICATE KEY UPDATE ";

	private static final String insertHeaderString = "INSERT INTO ";

	private static final String updateHeaderString = "UPDATE ";

	private static final String create = "CREATE TABLE ";

	private static final String ifNotExists = " IF NOT EXISTS ";

	private Database database = null;

	public void setDEBUG()
		{
			debug = true;
		}

	/**
	 * Create a new Table
	 * 
	 * @param theClass
	 *          the class to use to create the table object
	 * @param theDatabase
	 *          the database to use for call backs
	 * @param obj
	 *          an instance of the object to use (table name mappings)
	 */
	@SuppressWarnings("rawtypes")
	public DatabaseTable(Class theClass, Database theDatabase, Type obj)
		{
			super(theClass, obj);
			// tableClass = theClass;
			database = theDatabase;
		}

	/**
	 * Select the items from the database of this type(no where)
	 * 
	 * @return a list of all the objects
	 */
	public List<Type> select()
		{
			return select(null);
		}

	/**
	 * do a single select (one object)
	 * 
	 * @param where
	 *          {@link Where} clause to use
	 * @return a single item of the correct Type
	 */
	public Type selectSingle(Where where)
		{
			Type result = null;
			List<Type> list = selectInternal(where, null);
			if (list.size() > 0)
				{
					result = list.remove(0);
				}
			return result;
		}

	/**
	 * do a single select (one object)
	 * 
	 * @param where
	 *          {@link Where} clause to use
	 * @param Paramslist
	 *          list of parameters
	 * @return a single item of the correct Type
	 */
	public Type selectSingle(Where where, Object... Paramslist)
		{
			Type result = null;
			List<Type> list = select(where, Paramslist);
			if (list.size() > 0)
				{
					result = list.remove(0);
				}
			return result;
		}

	/**
	 * do a single select (one object)
	 * 
	 * @param where
	 *          where clause to use
	 * @param Paramslist
	 *          list of parameters
	 * @return a single item of the correct Type
	 */
	public Type selectSingle(Where where, LinkedList<Object> Paramslist)
		{
			Type result = null;
			List<Type> list = selectInternal(where, Paramslist);
			if (list.size() > 0)
				{
					result = list.remove(0);
				}
			return result;
		}

	/**
	 * Create the select statement for the given clauses
	 * 
	 * @param where
	 *          limits which fields are selected
	 * @param fields
	 *          the fields to fill in
	 * @return the string for this select
	 */
	public String getSelect(Where where, LinkedList<FieldData> fields)
		{
			StringBuffer select = getSelectAndFields(fields);
			if (where != null)
				select.append(" " + where.toSQL());
			if (debug)
				System.out.println(select.toString());
			return select.toString();
		}

	public String getSelectSpecial(String sql, String whereTableName, Where where, LinkedList<FieldData> fields)
		{
			StringBuffer select = getSelectAndFields(sql, whereTableName, fields);
			if (where != null)
				select.append(" " + where.toSQL());
			boolean manyField = false;
			for (Map.Entry<String, FieldData> data : mappingJavaToDB.entrySet())
				{
					String fieldName = data.getValue().databasename;
					if (!fieldName.startsWith("$"))// fields with a $ are internal
						{
							if (manyField)
								{
									// select.append(",");
								}
							else
								{
									manyField = true;
								}
							// select.append("`" + fieldName + "`");
							fields.addLast(data.getValue());
						}
				}
			return select.toString();
		}

	public String getSelect(OrderBy orderBy, LinkedList<FieldData> fields)
		{
			// StringBuffer select = new StringBuffer("SELECT ");
			// select.append("");
			// boolean manyField = false;
			// for (Map.Entry<String, FieldData> data : mappingJavaToDB.entrySet())
			// {
			// String fieldName = data.getValue().databasename;
			// if (!fieldName.startsWith("$"))// fields with a $ are internal
			// {
			// if (manyField)
			// {
			// select.append(",");
			// }
			// else
			// {
			// manyField = true;
			// }
			// select.append("`" + fieldName + "`");
			// fields.addLast(data.getValue());
			// }
			// }
			// select.append(" " + " FROM " + tableName);
			StringBuffer select = getSelectAndFields(fields);
			if (orderBy != null)
				select.append(" " + orderBy.toSQL());
			if (debug)
				System.out.println(select.toString());
			return select.toString();
		}

	public StringBuffer getSelectAndFields(LinkedList<FieldData> fields)
		{
			StringBuffer select = new StringBuffer("SELECT ");
			select.append("");
			boolean manyField = false;
			for (Map.Entry<String, FieldData> data : mappingJavaToDB.entrySet())
				{
					String fieldName = data.getValue().databasename;
					if (!fieldName.startsWith("$"))// fields with a $ are internal
						{
							if (manyField)
								{
									select.append(",");
								}
							else
								{
									manyField = true;
								}
							select.append("`" + fieldName + "`");
							fields.addLast(data.getValue());
						}
				}
			select.append(" " + " FROM " + tableName);
			return select;
		}

	public StringBuffer getSelectAndFields(String selectSpecial, String specialTable, LinkedList<FieldData> fields)
		{
			StringBuffer select = new StringBuffer("SELECT ");
			select.append("");
			select.append(selectSpecial);
			select.append(" FROM " + specialTable);

			return select;
		}

	/**
	 * Get the mapped fields
	 * 
	 * @return the mappings that were made during the creation of the table
	 */
	protected LinkedList<FieldData> getFields()
		{
			return new LinkedList<FieldData>(mappingJavaToDB.values());
		}

	/**
	 * Select the items from the database that match the where clause
	 * 
	 * @param where
	 *          the clause used to narrow down the results
	 * @return the list of items that match the where clause
	 */
	public List<Type> select(Where where)
		{
			return selectInternal(where, null);
		}

	/**
	 * Select the items from the database that match the where clause
	 * 
	 * @param where
	 *          the clause used to narrow down the results
	 * @param whereFields
	 *          the list of parameters
	 * @return the list of items that match the where clause
	 */
	public List<Type> select(Where where, Object... whereFields)
		{
			LinkedList<Object> list = new LinkedList<Object>();
			for (Object o : whereFields)
				{
					if (o instanceof Object[])
						{
							for (Object p : (Object[]) o)
								{
									list.addLast(p);
								}
						}
					else
						{
							list.addLast(o);
						}
				}
			return selectInternal(where, list);
		}

	public List<Type> selectSpecial(String sql, String parentTable, Where where, Object... whereFields)
		{
			LinkedList<Object> list = new LinkedList<Object>();
			for (Object o : whereFields)
				{
					if (o instanceof Object[])
						{
							for (Object p : (Object[]) o)
								{
									list.addLast(p);
								}
						}
					else
						{
							list.addLast(o);
						}
				}
			return selectInternalSpecial(sql, parentTable, where, list);
		}

	/**
	 * Select the items from the database that match the where clause
	 * 
	 * @param where
	 *          the clause used to narrow down the results
	 * @return the list of items that match the where clause
	 */
	private List<Type> selectInternal(Where where, LinkedList<Object> whereFields)
		{
			List<Type> result = new LinkedList<Type>();
			LinkedList<FieldData> fields = new LinkedList<FieldData>();
			String select = getSelect(where, fields);
			try
				{
					ResultSet data = database.getDatabaseConnection().queryDataBase(select, whereFields);
					while (data.next())
						{
							Type t = createInstance(data, fields, null);
							result.add(t);
						}
					data.close();
				}
			catch (Exception e)
				{
					e.printStackTrace();
				}
			return result;
		}

	private List<Type> selectInternalSpecial(String sql, String parentTable, Where where, LinkedList<Object> whereFields)
		{
			List<Type> result = new LinkedList<Type>();
			LinkedList<FieldData> fields = new LinkedList<FieldData>();
			String select = getSelectSpecial(sql, parentTable, where, fields);
			try
				{
					ResultSet data = database.getDatabaseConnection().queryDataBase(select, whereFields);

					while (data.next())
						{
							Type t = createInstance(data, fields, null);
							result.add(t);
						}
					data.close();
				}
			catch (Exception e)
				{
					e.printStackTrace();
				}
			return result;
		}

	public List<Type> selectSQL(String sql, Object[] whereFields)
		{
			List<Type> result = new LinkedList<Type>();
			LinkedList<FieldData> fields = new LinkedList<FieldData>();
			for (Map.Entry<String, FieldData> data : mappingJavaToDB.entrySet())
				{
					String fieldName = data.getValue().databasename;
					if (!fieldName.startsWith("$"))// fields with a $ are internal
						{
							fields.addLast(data.getValue());
						}
				}
			LinkedList<Object> whereFieldList = new LinkedList<Object>();
			whereFieldList.addAll(whereFieldList);
			try
				{
					ResultSet data = database.getDatabaseConnection().queryDataBase(sql, whereFieldList);
					while (data.next())
						{
							Type t = createInstance(data, fields, null);
							result.add(t);
						}
					data.close();
				}
			catch (Exception e)
				{
					e.printStackTrace();
				}
			return result;
		}

	public List<Type> selectInternal(OrderBy orderBy)
		{
			List<Type> result = new LinkedList<Type>();
			LinkedList<FieldData> fields = new LinkedList<FieldData>();
			String select = getSelect(orderBy, fields);
			try
				{
					ResultSet data = database.getDatabaseConnection().queryDataBase(select, new LinkedList<Object>());
					while (data.next())
						{
							Type t = createInstance(data, fields, null);
							result.add(t);
						}
					data.close();
				}
			catch (Exception e)
				{
					e.printStackTrace();
				}
			return result;
		}

	/**
	 * Do a select and map the primary keys
	 * 
	 * @param where
	 *          the {@link Where} clause to limit the results
	 * @param whereFields
	 *          the parameters for the query
	 * @return a mapping of primary keys to values
	 */
	public Map<Object, Type> selectInternalMap(Where where, LinkedList<Object> whereFields)
		{
			Map<Object, Type> result = new HashMap<Object, Type>();
			LinkedList<FieldData> fields = new LinkedList<FieldData>();
			String select = getSelect(where, fields);
			try
				{
					ResultSet data = database.getDatabaseConnection().queryDataBase(select, whereFields);
					while (data.next())
						{
							Type t = createInstance(data, fields, null);
							Object key = getData(t, keyName);
							result.put(key, t);
						}
					data.close();
				}
			catch (Exception e)
				{
					e.printStackTrace();
				}
			return result;
		}

	/**
	 * Insert a group of items into the database
	 * 
	 * @param items
	 *          the items to insert into the database
	 * @throws SQLException
	 */
	protected void insert(Object... items) throws SQLException
		{
			for (Object item : items)
				{
					insertItem(item);
				}
		}

	/**
	 * Delete the item from the database
	 * 
	 * @param item
	 *          the item to delete
	 */
	public void delete(Object item)
		{
			Where keyBasedWhere = new Where(Utilities.quoteString(keyName), OPERATOR.EQUAL, "?");
			Object value = getData(item, keyName);
			LinkedList<Object> list = new LinkedList<Object>();
			list.add(value);
			StringBuffer result = new StringBuffer(DELETE_FROM + Utilities.quoteString(tableName) + " " + keyBasedWhere.toSQL());
			try
				{
					database.getDatabaseConnection().updateDataBase(result.toString(), list);
				}
			catch (SQLException e)
				{
					e.printStackTrace();
				}
		}

	/**
	 * Delete an object from the database
	 * 
	 * @param item
	 *          the item to delete
	 * @param where
	 *          the Where clause
	 * @param argList
	 *          the arguments to the where clause
	 */
	public void delete(Object item, Where where, List<Object> argList)
		{
			StringBuffer result = new StringBuffer(DELETE_FROM + Utilities.quoteString(tableName) + " " + where.toSQL());
			try
				{
					database.getDatabaseConnection().updateDataBase(result.toString(), argList);
				}
			catch (SQLException e)
				{
					e.printStackTrace();
				}
		}

	public void deleteWhereOnly(Where where, List<Object> argList)
		{
			StringBuffer result = new StringBuffer(DELETE_FROM + Utilities.quoteString(tableName) + " " + where.toSQL());
			try
				{
					if (debug)
						{
							System.out.print(result.toString());
							for (Object o : argList)
								{
									System.out.print(" " + o.toString());
								}
							System.out.println();
						}
					database.getDatabaseConnection().updateDataBase(result.toString(), argList);
				}
			catch (SQLException e)
				{
					e.printStackTrace();
				}
		}

	/**
	 * Delete an object from the database
	 * 
	 * @param item
	 *          the item to delete
	 * @param where
	 *          the Where clause
	 * @param args
	 *          an VarArray of arguments to the where clause
	 */
	public void deleteWhere(Object item, Where where, Object... args)
		{
			List<Object> arguments = Arrays.asList(args);
			delete(item, where, arguments);
		}

	/**
	 * Get the update string
	 * 
	 * @param item
	 *          object to update from
	 * @return the pair that represents the update result
	 */
	private StringParameterPair getUpdateString(Object item)
		{
			StringParameterPair result = new StringParameterPair();
			StringBuffer update = new StringBuffer(DatabaseTable.update);
			// LinkedList<Object> updateValues = new LinkedList<Object>();
			for (Map.Entry<String, FieldData> data : mappingJavaToDB.entrySet())
				{
					String fieldName = data.getValue().databasename;
					if (!data.getValue().type.equals(FIELDTYPES.UserDefined))
						{
							Object value = getData(item, data.getKey());
							// update we don't care about the key
							if (!fieldName.equals(keyName))
								{
									if (!update.toString().equals(DatabaseTable.update))
										{
											update.append(",");
										}
									update.append("`" + fieldName + "`=?");
									if (value == null)
										{
											result.parameters.addLast(data.getValue().type);
										}
									else
										{
											result.parameters.addLast(value);
										}
								}
						}
				}
			result.queryString = update.toString();
			return result;
		}

	/**
	 * Insert the object into the database.
	 * 
	 * @param item
	 *          the object to save to the database. Defaults to updating the object if the key is a duplicate.
	 * @throws SQLException
	 */
	protected Object insertItem(Object item) throws SQLException
		{
			Object result = null;
			StringBuffer insertStatement = new StringBuffer(insertHeaderString + tableName);
			StringBuffer fieldList = new StringBuffer(" (");
			StringBuffer values = new StringBuffer(" VALUES (");
			LinkedList<Object> valueList = new LinkedList<Object>();
			Boolean hasKEy = (keyName != null && getData(item, keyName) == null);
			for (Map.Entry<String, FieldData> data : mappingJavaToDB.entrySet())
				{
					String fieldName = data.getValue().databasename;
					Boolean isEmptyKey = false;
					if (hasKEy)
						{
							if (fieldName.equals(keyName))
								{
									isEmptyKey = true;
								}
						}
					if (!data.getValue().type.equals(FIELDTYPES.UserDefined) && !isEmptyKey)
						{
							Object value = getData(item, data.getKey());
							if (!fieldList.toString().equals(" ("))
								{
									fieldList.append(",");
									values.append(",");
								}
							fieldList.append("`" + fieldName + "`");
							values.append("?");
							if (value == null)
								{
									valueList.addLast(data.getValue().type);
								}
							else
								{
									valueList.addLast(value);
								}
						}
				}
			fieldList.append(")");
			values.append(")");
			insertStatement.append(fieldList);
			insertStatement.append(values);
			// If we have a primary key, then care about the update, otherwise skip it
			if (keyName != null && getData(item, keyName) != null)
				{
					StringParameterPair pair = getUpdateString(item);
					for (Object o : pair.parameters)
						valueList.addLast(o);
					insertStatement.append(pair.queryString);
				}
			else if (keyName != null)
				{
					result = getData(item, keyName);
				}
			if (database != null)
				{
					if (debug)
						{
							System.out.println(insertStatement.toString() + " || " + valueList);
						}
					Integer result1 = database.getDatabaseConnection().updateDataBase(insertStatement.toString(), valueList);
					if (result1 != -1 && keyName != null)
						{
							setData(item, keyName, result1);
							result = result1;
						}
					else if (keyName != null)
						{
							try
								// if we didnt get a result, and the key is not null
								{

									result = getData(item, keyName);
								}
							catch (Exception e)
								{
									e.printStackTrace();
								}
						}
				}

			return result;
		}

	protected Integer updateItem(Object item, Where w, Object[] whereValues) throws SQLException
		{
			Integer result = null;
			LinkedList<Object> whereValueList = new LinkedList<Object>();
			if (whereValues != null && whereValues.length > 0)
				{
					whereValueList.addAll(Arrays.asList(whereValues));
				}
			StringBuffer insertStatement = new StringBuffer(updateHeaderString + tableName);
			StringBuffer values = new StringBuffer(" SET ");
			LinkedList<Object> valueList = new LinkedList<Object>();
			String del = "";
			for (Map.Entry<String, FieldData> data : mappingJavaToDB.entrySet())
				{
					String fieldName = data.getValue().databasename;

					if (!data.getValue().type.equals(FIELDTYPES.UserDefined))
						{
							Object value = getData(item, data.getKey());
							if (!fieldName.equals(keyName))
								{
									values.append(del + "`" + fieldName + "`= ?");

									if (value == null)
										{
											valueList.addLast(data.getValue().type);
										}
									else
										{
											valueList.addLast(value);
										}
									del = ",";
								}
							else if (fieldName.equals(keyName))
								{
									Where temp = new Where(Utilities.quoteString(tableName) + ".`" + fieldName + "`", Where.OPERATOR.EQUAL, "?");
									if (w != null)
										{
											w = Where.getBigAnd(w, temp);
										}
									else
										{
											w = temp;
										}
									whereValueList.addLast(value);
								}
						}
				}
			if (w != null) // we must have a where clause
				{
					values.append(" " + w.toSQL());
					valueList.addAll(whereValueList);
					insertStatement.append(values);
					insertStatement.append(" LIMIT 1 ");
					if (database != null)
						{
							if (debug)
								{
									System.out.println("|" + insertStatement.toString() + " |\n| " + valueList + "|");
								}
							result = database.getDatabaseConnection().updateOnly(insertStatement.toString(), valueList);
						}
				}
			return result;
		}

	public void createTable() throws SQLException, InstantiationException, IllegalAccessException
		{
			LinkedList<String> children = new LinkedList<String>();
			createTable(null, new LinkedList<DatabaseTable<Type>>(), null, children);
			for (String child : children)
				{
					if (debug)
						System.out.println(child);
					database.getDatabaseConnection().updateDataBase(child, null);
				}
		}

	@SuppressWarnings("unchecked")
	private void createTable(Field fk, LinkedList<DatabaseTable<Type>> tables, String className, LinkedList<String> list) throws SQLException, InstantiationException, IllegalAccessException
		{
			StringBuffer createStatement = new StringBuffer(create + ifNotExists);
			JavaSQLConversion conversoin = new JavaSQLConversion();
			LinkedList<String> children = new LinkedList<String>();
			createStatement.append("`" + tableName + "`");
			// Field[] fields = c.getDeclaredFields();
			createStatement.append("(");
			String tail = "";
			String delimitier = "";
			if (fk != null)
				{
					createStatement.append(" `" + className + "_FK` " + FIELDTYPES.INT.getSQL(null) + ",");
				}
			for (Field f : getListOfFields())
				{
					if (f.getAnnotation(transientVar.class) == null)
						{
							if (debug)
								System.err.println(f.getType().getCanonicalName());
							FIELDTYPES type = conversoin.getJavaType(f.getType().getCanonicalName());
							if (type != FIELDTYPES.UserDefined)
								{
									createStatement.append(delimitier);
									delimitier = ",";
									String fieldName = f.getName();
									String dbName = mappingJavaToDB.get(fieldName).databasename;
									Integer len = null;
									if (f.getAnnotation(length.class) != null)
										{
											len = Integer.parseInt(f.getAnnotation(length.class).value());
										}
									createStatement.append(" `" + dbName + "` ");
									createStatement.append(type.getSQL(len));
									if (f.getAnnotation(primarykey.class) != null)
										{
											tail += ", PRIMARY KEY (`" + dbName + "`)";
											createStatement.append(" NOT NULL");
											if (f.getAnnotation(autoincrement.class) != null)
												{
													createStatement.append(" auto_increment ");
												}
										}
									else
										{
											createStatement.append(" DEFAULT NULL");
										}
								}
							else
								{
									DatabaseTable<Type> t = (DatabaseTable<Type>) this.database.getTable(f.getType().newInstance());
									if (!tables.contains(t))
										{
											tables.add(t);
											t.createTable(f, tables, tableName + "_" + keyName, children);
										}
								}
						}
				}
			createStatement.append(tail);
			createStatement.append(")");
			list.add(createStatement.toString());
			list.addAll(children);
		}

	/**
	 * A clone that recursively clones the objects
	 * 
	 * @param <S>
	 *          type of object to clone
	 * @param otherObject
	 *          the object to clone at this level
	 * @param classes
	 *          the set of class mappings
	 * @return the new object and all the children that have been cloned
	 */
	// protected <S> Type deepClone(S otherObject, ClassPair... classes)
	// {
	// Type result = cloneSameFields(otherObject);
	// if (otherObject instanceof Children && result instanceof Children)
	// {
	// Children resultChild = ((Children) result);
	// for (ClassPair cc : classes)
	// {
	// List<?> children = ((Children) otherObject).getChildren(cc.getLeft());
	// DatabaseTable<?> childTable = database.getTable(cc.getRight());
	// if (children != null)
	// {
	// for (Object child : children)
	// {
	// Object newChild = childTable.deepClone(child, classes);
	// resultChild.addChild(newChild);
	// }
	// }
	// }
	// }
	// return result;
	// }

	// /**
	// * Clone an object (cloning the fields if they are the some in the DB)
	// *
	// * @param <S>
	// * the type of the object
	// * @param otherObject
	// * the object to clone
	// * @return the new object from this table
	// */
	// @SuppressWarnings("unchecked")
	// private <S> Type cloneSameFields(S otherObject)
	// {
	// Type result = null;
	// DatabaseTable<S> other = database.getTable(otherObject);
	// try
	// {
	// List<FieldData> otherFields = other.getFields();
	// result = (Type) Class.forName(tableClass.getCanonicalName()).newInstance();
	// for (FieldData f : getFields())
	// {
	// for (FieldData t : otherFields)
	// {
	// if (t.databasename.equals(f.databasename) && !f.javaname.equals(keyName))
	// {
	// setData(result, f.javaname, getData(otherObject, t.javaname));
	// break;
	// }
	// }
	// }
	// // set the clonedItem so we can reference it later
	// // ((Children) otherObject).setClonedItem((Children) result);
	// // ((Children) result).setClonedItem((Children) otherObject);
	// }
	// catch (Exception e)
	// {
	// e.printStackTrace();
	// }
	//
	// return result;
	// }

	/**
	 * Given a result set, we will get the data from that {@link ResultSet}
	 * 
	 * @param r
	 *          the {@link ResultSet} to get the value from
	 * @param fieldData
	 *          the type and name to get from the result set
	 * @param fullName
	 *          TODO
	 * @return the correct value from the result set
	 * @throws SQLException
	 */
	@SuppressWarnings("unchecked")
	private Type getValue(ResultSet r, FieldData fieldData, String fullName) throws SQLException
		{
			Type result = null;
			String fieldName = fieldData.databasename;
			if (fullName != null)
				{
					fieldName = fullName + "." + fieldName;
				}
			switch (fieldData.type)
				{
				case UserDefined:
					// if (fieldData.optionalType != null)
					// {
					// Calendar temp = Calendar.getInstance();
					// Date d = null;
					// try
					// {
					// d = r.getDate(fieldName);
					// }
					// catch (Exception e)
					// {
					// }
					// if (d != null)
					// {
					// temp.setTime(d);
					// result = (Type) temp;
					// }
					//
					// }
					break;
				case BOOL:
					result = (Type) new Boolean(r.getBoolean(fieldName));
					try
						{
							if (r.getInt(fieldName) == -1)
								{
									if (result != null)
										{
											result = (Type) Boolean.TRUE;
										}
								}
						}
					catch (Exception e)
						{
							// e.printStackTrace();
						}
					if (((Boolean) result) == false)
						{
							if (r.getObject(fieldName) == null)
								{
									result = null;
								}
						}
					break;
				case DOUBLE:
					result = (Type) new Double(r.getDouble(fieldName));
					Object tempD = r.getObject(fieldName);
					if (tempD == null)
						{
							result = null;
						}
					break;

				case DECIMAL:
					// result = (Type) new Double(r.getDouble(fieldName));
					result = (Type) r.getBigDecimal(fieldName);
					break;
				case INT:
					// result = (Type) r.getObject(fieldName);
					result = (Type) new Integer(r.getInt(fieldName));
					Object tempI = r.getObject(fieldName);
					if (tempI == null)
						{
							result = null;
						}
					break;
				case STRING:
					result = (Type) r.getString(fieldName);
					break;
				case LONG:
					// result = (Type) r.getObject(fieldName);
					result = (Type) new Long(r.getLong(fieldName));
					Object tempO = r.getObject(fieldName);
					if (tempO == null)
						{
							result = null;
						}
					break;
				case TIMESTAMP:
					result = (Type) r.getTimestamp(fieldName);
					break;
				case Date:
					// Calendar temp = Calendar.getInstance();
					Date d = null;
					try
						{
							d = r.getDate(fieldName);
						}
					catch (Exception e)
						{
						}
					if (d != null)
						{
							result = (Type) d;
							// temp.setTime(d);
							// result = (Type) temp;
						}
					break;
				case Blob:
					result = (Type) r.getBlob(fieldName);
					break;
				default:
					break;
				}
			return result;
		}

	/**
	 * Create an Instance of the base Type of the table
	 * 
	 * @param data
	 * @param fields
	 * @param fullName
	 *          TODO
	 * @return a new instance with the corresponding fields from the database initialized with the values from the result set
	 */
	@SuppressWarnings("unchecked")
	public Type createInstance(ResultSet data, List<FieldData> fields, String fullName)
		{
			Type result = null;
			try
				{
					result = (Type) Class.forName(tableClass.getCanonicalName()).newInstance();
					for (FieldData f : fields)
						{
							Object value = getValue(data, f, fullName);
							setData(result, f.javaname, value);
							if (result instanceof DatabaseRow && f.javaname.equals(keyName))
								{
									((DatabaseRow) result).setPrimaryKey(value);
								}
						}
				}
			catch (Exception e)
				{
					e.printStackTrace();
				}
			return result;
		}

	public static void setPrimaryKey(Object o, Object value)
		{

			if (o instanceof DatabaseRow)
				{
					((DatabaseRow) o).setPrimaryKey(value);
				}
			else
				{
					Set<Field> methods = DatabaseTable.getListOfFields(o.getClass());
					for (Field m : methods)
						{
							if (m.getAnnotation(primarykey.class) != null)
								{
									try
										{
											m.setAccessible(true);
											m.set(o, value);
										}
									catch (Exception e)
										{
											e.printStackTrace();
										}
								}
						}
				}
		}

}
